package jfparted;

/**
 *
 * Created : Apr 13, 2012
 *
 * @author pquiring
 */

import java.io.*;
import javax.swing.table.*;
import java.util.*;

import javaforce.*;

public class PartitionEditorPanel extends IPanel {

  /**
   * Creates new form Partition
   */
  public PartitionEditorPanel(boolean mountNow) {
    initComponents();
    tableModel = (DefaultTableModel)table.getModel();
    this.mountNow = mountNow;
    showPartitions();
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jToolBar1 = new javax.swing.JToolBar();
    add = new javax.swing.JButton();
    delete = new javax.swing.JButton();
    change = new javax.swing.JButton();
    rescan = new javax.swing.JButton();
    jLabel1 = new javax.swing.JLabel();
    image = new javax.swing.JPanel();
    jScrollPane1 = new javax.swing.JScrollPane();
    table = new javax.swing.JTable();

    jToolBar1.setFloatable(false);
    jToolBar1.setRollover(true);

    add.setText("Add");
    add.setFocusable(false);
    add.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
    add.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
    add.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        addActionPerformed(evt);
      }
    });
    jToolBar1.add(add);

    delete.setText("Delete");
    delete.setFocusable(false);
    delete.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
    delete.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
    delete.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        deleteActionPerformed(evt);
      }
    });
    jToolBar1.add(delete);

    change.setText("Change");
    change.setFocusable(false);
    change.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
    change.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
    change.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        changeActionPerformed(evt);
      }
    });
    jToolBar1.add(change);

    rescan.setText("Rescan");
    rescan.setFocusable(false);
    rescan.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
    rescan.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
    rescan.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        rescanActionPerformed(evt);
      }
    });
    jToolBar1.add(rescan);

    jLabel1.setFont(new java.awt.Font("Ubuntu", 0, 12)); // NOI18N
    jLabel1.setText("Note : All operations are applied instantly.");
    jToolBar1.add(jLabel1);

    image.setLayout(new java.awt.GridLayout(1, 0));

    table.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {

      },
      new String [] {
        "Device", "Type", "Moint Point", "Size"
      }
    ) {
      Class[] types = new Class [] {
        java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
      };
      boolean[] canEdit = new boolean [] {
        false, false, false, false
      };

      public Class getColumnClass(int columnIndex) {
        return types [columnIndex];
      }

      public boolean isCellEditable(int rowIndex, int columnIndex) {
        return canEdit [columnIndex];
      }
    });
    table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
    table.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        tableMouseClicked(evt);
      }
    });
    table.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyTyped(java.awt.event.KeyEvent evt) {
        tableKeyTyped(evt);
      }
    });
    jScrollPane1.setViewportView(table);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(jToolBar1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 666, Short.MAX_VALUE)
          .addComponent(jScrollPane1))
        .addContainerGap())
      .addComponent(image, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(image, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addContainerGap())
    );
  }// </editor-fold>//GEN-END:initComponents

  private void addActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addActionPerformed
    addPart();
  }//GEN-LAST:event_addActionPerformed

  private void deleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteActionPerformed
    deletePart();
  }//GEN-LAST:event_deleteActionPerformed

  private void changeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeActionPerformed
    changePart();
  }//GEN-LAST:event_changeActionPerformed

  private void tableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tableMouseClicked
    updateDeviceImage();
  }//GEN-LAST:event_tableMouseClicked

  private void tableKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_tableKeyTyped
    updateDeviceImage();
  }//GEN-LAST:event_tableKeyTyped

  private void rescanActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rescanActionPerformed
    showPartitions();
  }//GEN-LAST:event_rescanActionPerformed

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton add;
  private javax.swing.JButton change;
  private javax.swing.JButton delete;
  private javax.swing.JPanel image;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JToolBar jToolBar1;
  private javax.swing.JButton rescan;
  private javax.swing.JTable table;
  // End of variables declaration//GEN-END:variables

  private DefaultTableModel tableModel;
  private ArrayList<Object> rows;
  private ArrayList<Data.Device> devices2;
  private boolean mountNow;

  //Device, Type(filesys), MountPoint, Size
  public void showPartitions() {
    Data.getDevices();
    for(int idx=0;idx<Data.devices.size();idx++) {
      if (Data.devices.get(idx).uninit) {
        if (JFAWT.showConfirm("Warning", "Device " + Data.devices.get(idx).dev + " needs a partition table.\rCreate it now?")) {
          Data.createPartTable(Data.devices.get(idx).dev);
          showPartitions();
        }
        return;
      }
    }
    Data.getPartitions();
    String mounts[] = null;
    if (mountNow) {
      ShellProcess sp = new ShellProcess();
      String output = sp.run(new String[] {"mount"}, false);
      mounts = output.split("\n");
    }
    while (tableModel.getRowCount() > 0) tableModel.removeRow(0);
    rows = new ArrayList<Object>();
    for(int d=0;d<Data.devices.size();d++) {
      Data.Device device = Data.devices.get(d);
      tableModel.addRow(new Object[] {device.dev,"","",device.size});
      rows.add(device);
      for(int p=0;p<device.parts.size();p++) {
        Data.Partition part = device.parts.get(p);
        if ((part.number != -1) && (devices2 != null)) {
          //copy over mount points
          for(int d2=0;d2<devices2.size();d2++) {
            Data.Device device2 = devices2.get(d2);
            for(int p2=0;p2<device2.parts.size();p2++) {
              Data.Partition part2 = device2.parts.get(p2);
              if ((part.device.dev + part.device).equals(part2.device.dev + part2.number)) {
                part.mount = part2.mount;
              }
            }
          }
        }
        if ((mountNow) && (part.number != -1)) {
          for(int m=0;m<mounts.length;m++) {
            String f[] = mounts[m].split(" ");
            if (f.length != 6) continue;
            //0=dev 1="on" 2=mount 3="type" 4=fstype 5=options
            if (f[0].equals(part.device.dev + part.number)) {
              part.mount = f[2];
            }
          }
        }
        tableModel.addRow(
          new Object[] {(part.number == -1) ? "" : part.device.dev + part.number,
          part.filesys,part.mount,part.size});
        rows.add(part);
      }
    }
    devices2 = Data.devices;  //backup for next rescan
  }

  public void showDeviceImage(Data.Device device) {
    try {
      DeviceImage di = new DeviceImage(device);
      image.add(di);
      image.revalidate();
      image.repaint();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private void updateDeviceImage() {
    image.removeAll();
    if (table.getSelectedRowCount() != 1) return;
    int idx = table.getSelectedRow();
    while (idx >= 0) {
      Object obj = rows.get(idx);
      if (obj instanceof Data.Device) {
        showDeviceImage((Data.Device)obj);
        return;
      }
      idx--;
    }
  }

  private void addPart() {
    if (table.getSelectedRowCount() != 1) return;
    int idx = table.getSelectedRow();
    Object obj = rows.get(idx);
    if (obj instanceof Data.Device) return;
    Data.Partition part = (Data.Partition)obj;
    if (part.number != -1) return;
    String orgMountPoint = part.mount;
    ModifyPartition dialog = new ModifyPartition(null, true, part, false, mountNow);
    dialog.setVisible(true);
    if (!dialog.accepted) return;
    if (!Data.createPart(part.device, dialog.gettype(), dialog.getfilesys(), dialog.getstart(), dialog.getend())) {
      JFAWT.showError("Error", "Failed to create Partition");
    }
    String mount = dialog.getmount();
    if (mount.equals("none")) mount = "";
    part.mount = "";
    for(int d=0;d<Data.devices.size();d++) {
      Data.Device device = Data.devices.get(d);
      for(int p=0;p<device.parts.size();p++) {
        Data.Partition part2 = device.parts.get(p);
        if (part2.number == -1) continue;
        if (part2.mount.length() == 0) continue;
        if (part2.mount.equals(mount)) {
          JFAWT.showError("Warning", "That mount point is already in use");
          mount = "";
        }
      }
    }
    part.mount = mount;
    part.filesys = dialog.getfilesys();
    part.label = dialog.getlabel();
    if (dialog.getformat()) {
      formatPart(part, orgMountPoint);
    }
    if (mountNow) {
      if (!mount(orgMountPoint, part)) JFAWT.showError("Warning", "Failed to update mount point");
    }
    showPartitions();  //rescan
    if (mountNow) {
      if (dialog.getgenfstab()) genfstab();
    }
  }

  private boolean mount(String orgMountPoint, Data.Partition part) {
    if (orgMountPoint.equals(part.mount)) return true;
    if (orgMountPoint.length() > 0) {
      //try to unmount it
      ShellProcess sp = new ShellProcess();
      sp.run(new String[] {"sudo", "umount", part.device.dev + part.number}, false);
      if (sp.getErrorLevel() > 0) return false;
    }
    if (part.mount.length() == 0) return true;
    ShellProcess sp = new ShellProcess();
    sp.run(new String[] {"sudo", "mount", part.device.dev + part.number, part.mount}, false);
    if (sp.getErrorLevel() > 0) return false;
    return true;
  }

  private void deletePart() {
    if (table.getSelectedRowCount() != 1) return;
    int idx = table.getSelectedRow();
    Object obj = rows.get(idx);
    if (obj instanceof Data.Device) return;
    Data.Partition part = (Data.Partition)obj;
    if (part.number == -1) return;  //can not delete
    if ((mountNow) && (part.mount.length() > 0)) {
      JFAWT.showError("Error", "Unmount partition first");
      return;
    }
    if (!JFAWT.showConfirm("Confirm",
      "Are you sure you want to delete '" + part.device.dev + part.number  + "' ?"
      + "\nThis action will be performed now and can not be undone.")) return;
    if (!Data.deletePart(part)) {
      JFAWT.showError("Error", "Failed to delete Partition");
    }
    showPartitions();  //rescan
  }

  private void changePart() {
    if (table.getSelectedRowCount() != 1) return;
    int idx = table.getSelectedRow();
    Object obj = rows.get(idx);
    if (obj instanceof Data.Device) return;
    Data.Partition part = (Data.Partition)obj;
    if (part.number == -1) return;  //can not modify
    String orgMountPoint = part.mount;
    ModifyPartition dialog = new ModifyPartition(null, true, part, true, mountNow);
    dialog.setVisible(true);
    if (!dialog.accepted) return;
    String mount = dialog.getmount();
    if (mount.equals("none")) mount = "";
    part.mount = "";
    for(int d=0;d<Data.devices.size();d++) {
      Data.Device device = Data.devices.get(d);
      for(int p=0;p<device.parts.size();p++) {
        Data.Partition part2 = device.parts.get(p);
        if (part2.number == -1) continue;
        if (part2.mount.length() == 0) continue;
        if (part2.mount.equals(mount)) {
          JFAWT.showError("Warning", "That mount point is already in use");
          mount = "";
        }
      }
    }
    part.mount = mount;
    part.filesys = dialog.getfilesys();
    part.label = dialog.getlabel();
    if (dialog.getformat()) {
      formatPart(part, orgMountPoint);
    }
    if (mountNow) {
      if (!mount(orgMountPoint, part)) JFAWT.showError("Warning", "Failed to update mount point");
    }
    showPartitions();  //rescan
    if (mountNow) {
      if (dialog.getgenfstab()) genfstab();
    }
  }

  private void formatPart(Data.Partition part, String orgMountPoint) {
    JFLog.log("formatPart:" + part.device.dev + part.number + " with " + part.filesys);
    JFTask task = new JFTask() {
      private Data.Partition part;
      private int total = 25, done = 0;
      public boolean work() {
        this.part = (Data.Partition)this.getProperty("part");
        this.setLabel("Formating partition...");
        boolean status = Data.format(part, this);
        setProgress(100);
        if (!status) {
          setLabel("Error : Failed to format parition");
        } else {
          setLabel("Success : Partition formatted");
        }
        return status;
      }
      public void shellProcessOutput(String str) {
        JFLog.log(str);
        done += str.split("\n").length - 1;
        if (done > total) total += 25;
        setProgress(done * 100 / total);
      }
    };
    task.setProperty("part", part);
    ProgressDialog dialog = new ProgressDialog(null, true, task);
    dialog.setVisible(true);
    if (!dialog.getStatus()) {
      JFAWT.showError("Error", "Failed to format partition");
      return;
    }
    if (mountNow) {
      if (!mount(orgMountPoint, part)) JFAWT.showError("Warning", "Failed to update mount point");
    }
  }

  private boolean genfstab() {
    //generate fstab records
    Data.clearfstab();
    for(int d=0;d<Data.devices.size();d++) {
      Data.Device device = Data.devices.get(d);
      for(int p=0;p<device.parts.size();p++) {
        Data.Partition part = device.parts.get(p);
        if (part.number != -1) {
          if (part.mount.length() == 0) continue;
          if (part.mount.equals("/")) {
            Data.root = part;
            Data.addfstab(part.device.dev + part.number, part.mount, part.filesys,
              "errors=remount-ro", 0, 1);
          } else if (part.mount.equals("swap")) {
            Data.swap = part;
            part.mount = "none";
            Data.addfstab(part.device.dev + part.number, part.mount, part.filesys,
              "sw", 0, 0);
          } else {
            Data.addfstab(part.device.dev + part.number, part.mount, part.filesys,
              "rw", 0, 0);
          }
        }
      }
    }
    try {
      File tmpFile = new File("/tmp/fstab");
      FileOutputStream fos = new FileOutputStream(tmpFile);
      fos.write("#fstab generated by jinstall\n".getBytes());
      for(int a=0;a<Data.fstab.size();a++) {
        fos.write((Data.fstab.get(a) + "\n").getBytes());
      }
      fos.close();
      ShellProcess sp = new ShellProcess();
      ArrayList<String> cmd = new ArrayList<String>();
      cmd.add("sudo");
      cmd.add("cp");
      cmd.add("/tmp/fstab");
      cmd.add("/mnt/install/etc/fstab");
      String output = sp.run(cmd, false);
      if ((output == null) || (sp.getErrorLevel() != 0)) {
        JFLog.log("ErrorLevel=" + sp.getErrorLevel());
        return false;
      }
      tmpFile.delete();
    } catch (Exception e) {
      JFLog.log(e);
      return false;
    }
    return true;
  }

  //these methods are overridden in jinstall.CustomPartitioning

  public IPanel next() {
    return null;
  }

  public IPanel prev() {
    return null;
  }

  public IPanel getThis() {
    return this;
  }
}
